#ifndef CONTROLLER_H
#define CONTROLLER_H

#include "../network/Network.h"

#include "Model.h"

using namespace Network;

namespace Client {

/**
 *
 **/
struct FileDesc {
	QString fname;
	int size;
	QByteArray hash;
	Common::FileType type;
};

/**
 * Model danych przedstawiający listę wyników wyszukiwania
 **/
class SearchResultModel : public QAbstractItemModel {
	Q_OBJECT

public:
	/**
	 * Konstruktor.
	 *
	 * @param ref referencja na obserwowaną listę plików
	 **/
	SearchResultModel(QList<FileDesc> & ref, QObject * parent = 0);

	/**
	 * Ilość plików
	 **/
	int rowCount(const QModelIndex & parent = QModelIndex()) const;

	/**
	 * Ilośc kolumn
	 **/
	int columnCount(const QModelIndex & parent = QModelIndex()) const;

	/**
	 * Dane związane z konkretnym plikiem wskazywanym przez index
	 **/
	QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const;

	/**
	 * Indeks
	 **/
	QModelIndex index(int row, int column, const QModelIndex &parent) const;

	QModelIndex parent(const QModelIndex &index) const;

	Qt::ItemFlags flags(const QModelIndex &index) const;

	QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;

private:
	/// referencja na spis udostępnianych plików
	QList<FileDesc> & search_;
};

/**
 * @brief Koordynacja działań klienta
 *
 * Podobnie jak w serwerze, moduł ten odpowiada za przekazywanie informacji pomiędzy
 * kontrolerem sieciowym a modelem klienta. Dodatkowo jednak zajmuje się też obsługą
 * komunikatów odebranych z GUI klienta, a więc reakcją na działania użytkownika. Spełnia
 * zarówno zadania części serwerowej klienta (obsługując żądania przesłania plików
 * napływające od innych klientów), jak też części klienckiej (obsługując żądania użytkownika
 * i wysyłając żądania do głównego serwera i innych klientów). Kontroler zarządza otwartymi
 * połączeniami, nie dopuszczając do przekroczenia zdefiniowanej liczby jednocześnie
 * otwartych gniazd.
 **/
class Controller : public QObject
{
	Q_OBJECT

public:
	/**
	 * Zainicjowanie modelu oraz wszystkich danych związanych z klientem
	 **/
    Controller();

	~Controller();

	/**
	 * Zainicjowanie połączenia z głównym serwerem.
	 *
	 * @param host adres serwera
	 * @param port port serwera
	 *
	 * @return true, jeśli połączenie zostało nawiązane
	 **/
	bool start(QString host = "127.0.0.1", int port = 52347);

	/**
	 * Zakończenie pracy kontrolera. Rozłączenie wszystkich klientów,
	 * powiadomienie modelu o konieczności zapisania danych.
	 **/
	void stop();

	/*
		funkcje wywoływane bezpośrednio z interfejsu użytkownika
	*/

	/**
	 * Wysłanie do serwera pakietu ptFind z zapytaniem.
	 **/
	void find(QString query, Common::FileType type, int size_min, int size_max);

	/**
	 * Rozpoczyna pobieranie wybranego pliku.
	 **/
	void download(FileDesc * file);

	/**
	 * Dodaje nowe pliki do listy udostępnianych. Typ pliku jest ustawiany na podstawie rozszerzenia.
	 * Suma kontrolna jest wyliczana w modelu.
	 **/
	void addSharedFile(QString fname);

	/**
	 * Wysyła informację o nowych plikach do serwera
	 **/
	void sendAddedFiles();

	/*
		Dodatkowe metody
	*/

	/**
	 * Wskazanie na połączenie z głównym serwerem
	 **/
	RemoteClient * mainServer();

	SharedFilesModel * getSharedFilesModel() {
		return sharedFilesModel;
	}

	SearchResultModel * getSearchResultModel() {
		return searchResultModel;
	}

	JobsModel * getJobsModel() {
		return jobsModel;
	}

	/**
	 *
	 **/
	QList<FileDesc> & searchResult() {
		return search_;
	}

private:
	/**
	 * Reakcja na pakiet ptGet z żądaniem wysłania fragmentu pliku
	 **/
	Packet * get(Packet * packet);

	/**
	 * Inny klient wysłał fragment pliku
	 **/
	void give(Packet * packet);

	/**
	 * Serwer przydzielił klientowi nowy identyfikator
	 **/
	void newID(Packet * packet);

	/**
	 * Serwer wysłał
	 **/
	void list(Packet * packet);

	void listSeeds(Packet * packet);


	/// Model
	Model model;

	/// Połączenie z głównym serwerem
	RemoteClient * main_server;

	/// kontroler sieci
	Network::Controller ncontroller;

	/// adres serwera
	QString shost;

	/// numer portu serwera
	int sport;

	/// model danych dla spisu udostępnianych plików
	SharedFilesModel * sharedFilesModel;

	/// model danych dla spisu wyników wyszukiwania
	SearchResultModel * searchResultModel;

	/// model danych dla spisu zadań
	JobsModel * jobsModel;

	/// lista dodanych plików czekająca na wysłanie do serwera
	QList<SharedFileDesc *> shared_;

	/// lista wyników ostatniego wyszukiwania
	QList<FileDesc> search_;

public slots:
	/**
	 * Z sieci odebrany został nowy pakiet. Jest on rozpakowywany, a następnie
	 * w zależności od typu przekazywany do odpowiednich metod. Po wykonaniu
	 * koniecznych operacji (odwołania do bazy danych itp), do klienta wysyłana
	 * jest odpowiedź (o ile istnieje taka konieczność).
	 *
	 * @param client klient, od którego odebrano pakiet
	 **/
	void onNewPacket(RemoteClient * client);

	/**
	 * Podłączył się nowy klient
	 *
	 * @param client wskazanie na nowego klienta
	 **/
	void onNewClient(RemoteClient * client);

	/**
	 * Klient zmarł...
	 **/
	void onClientDied(RemoteClient * client);

	/**
	 * Próba ponownego połączenia z serwerem
	 **/
	void reconnect();

	/**
	 * Funkcja wywoływana w regularnych odstępach czasu sprawdza stan wszystkich zadań
	 * pobierania plików, i w razie konieczności nawiązuje nowe połączenia
	 **/
	void handleJobs();

signals:
	/**
	 * Połączenie z głównym serwerem zostało nawiązane
	 **/
	void serverConnected();

	/**
	 * Połączenie z głównym serwerem zostało zerwane
	 **/
	void serverDisconnected();

	/**
	 * Komunikat wysyłany do systemu logującego zdarzenia.
	 *
	 * @param msg treść komunikatu
	 * @param type typ komunikatu
	 **/
	void newMsg(QString msg, int type);

	/**
	 *
	 **/
	void updateSearchResult();
};

} // namespace Client

#endif // CONTROLLER_H
